package app.engine.component;

import app.engine.entitys.Move;
import app.engine.enums.ItemType;
import app.game.ChessBoard;
import app.game.enums.Chess;
import app.ui.Config;

import javax.sound.midi.Soundbank;
import java.util.Date;
import java.util.Random;

/**
 * @Description TODO
 * @Date 2023/7/7 1:19
 * @Created by LYP
 */
public class TranspositionTable implements Config {

    long[][][] hash64Table;
    int[][][] hash32Table;

    // 32位哈希值，用于置换表查找时的key
    int boardHash32;
    // 64位哈希值，用于校验是否冲突
    long boardHash64;
    // 置换表，31位正整数作为key
    TableItem[] hashTable;

    public static final int NOT_HIT = -99999999;

    public class TableItem {
        // 置换表校验值，如果命中则通过校验位避免冲突
        long checkKey64;
        // 节点类型
        ItemType itemType;
        // 评分
        int score;
        // 深度
        int depth;

        public TableItem(ItemType itemType, int score, int depth) {
            this.itemType = itemType;
            this.score = score;
            this.depth = depth;
            this.checkKey64 = boardHash64;
        }
    }

    public TranspositionTable() {

    }

    public static void main(String[] args) {
        TranspositionTable transpositionTable = new TranspositionTable();
        transpositionTable.hashTable = new TableItem[1 << 28];
        TranspositionTable.TableItem tableItem = transpositionTable.new TableItem(ItemType.EXACT, 0, 1);
        System.out.println(tableItem.hashCode());
        transpositionTable.hashTable[0] = tableItem;
        System.out.println(transpositionTable.hashTable[0].hashCode());
    }

    /**
     * 初始化置换表
     */
    public void init(Chess[][] chessBoard) {
        // 设置当前时间为随机数种子
        Random random = new Random(new Date().getTime());
        // 初始化棋盘每个点位的哈希值
        hash64Table = new long[3][BOARD_LINE][BOARD_LINE];
        hash32Table = new int[3][BOARD_LINE][BOARD_LINE];
        for (int i = 0; i < BOARD_LINE; i ++) {
            for (int j = 0; j < BOARD_LINE; j ++) {
                for (int k = 0; k < 3; k ++) {
                    hash64Table[k][i][j] = random.nextLong();
                    hash32Table[k][i][j] = random.nextInt();
                }
            }
        }
        // 计算当前棋盘对应的哈希值
        boardHash32 = 0;
        boardHash64 = 0;
        for (int i = 0; i < BOARD_LINE; i ++) {
            for (int j = 0; j < BOARD_LINE; j++) {
                boardHash32 ^= hash32Table[chessBoard[i][j].getType()][i][j];
                boardHash64 ^= hash64Table[chessBoard[i][j].getType()][i][j];
            }
        }
        // 给置换表申请内存
        hashTable = new TableItem[1 << 28];
        //System.out.println("置换表初始化后boardHash32:"+boardHash32);
    }

    public void hashPutChess(Move move, Chess chess) {
        //System.out.println("hashMove["+move.getX()+","+move.getY()+"]置换表更新前boardHash32:"+boardHash32 + "，" + hash32Table[chess.getType()][move.getX()][move.getY()]);
        boardHash32 ^= hash32Table[chess.getType()][move.getX()][move.getY()];
        boardHash64 ^= hash64Table[chess.getType()][move.getX()][move.getY()];
        //System.out.println("hashMove["+move.getX()+","+move.getY()+"]置换表更新后boardHash32:"+boardHash32);
    }

    public void hashPutBackChess(Move move) {
        //System.out.println("hashMoveBack["+move.getX()+","+move.getY()+"]置换表更新前boardHash32:"+boardHash32 + "，" + hash32Table[Chess.EMPTY.getType()][move.getX()][move.getY()]);
        boardHash32 ^= hash32Table[Chess.EMPTY.getType()][move.getX()][move.getY()];
        boardHash64 ^= hash64Table[Chess.EMPTY.getType()][move.getX()][move.getY()];
        //System.out.println("hashMoveBack["+move.getX()+","+move.getY()+"]置换表更新后boardHash32:"+boardHash32);
    }

    public int searchHashTable(int alpha, int beta, int depth) {
        // 生成28位地址，直接拿到置换表的元素
        TableItem item = hashTable[boardHash32 & 0xFFFFFFF];
        // 为空或校验位不一致，未命中
        if (item == null || item.checkKey64 != boardHash64) {
            return NOT_HIT;
        }
        // 只有当置换表项的深度大于等于当前深度才用，否则是迭代加深搜索的历史数据不能用
        if (item.depth >= depth) {
            switch (item.itemType) {
                // 如果上个节点是叶子节点或者完全搜索的准确值，则直接返回之前的分数
                case EXACT:
                    return item.score;
                // 如果上个节点发生了剪枝，则只知道至少有这个最好评分（下界），如果按照目前窗口仍然剪枝，则
                // 直接返回分数，否则需要重新搜索
                case LOWBOUND:
                    if (item.score >= beta) {
                        return item.score;
                    }
                    break;
                // 如果上个节点不是兄弟节点中最好的（PV节点），则知道最多有这么大的评分（上界），如果按照
                // 目前窗口仍然不是最好节点，则直接返回分数，否则需要重新搜索
                case UPPERBOUND:
                    if (item.score <= alpha) {
                        return item.score;
                    }
                    break;
            }
        }
        return NOT_HIT;
    }

    public void insertHashTable(ItemType itemType, int score, int depth) {
        TableItem tableItem = new TableItem(itemType, score, depth);
        hashTable[boardHash32 & 0xFFFFFFF] = tableItem;
    }
}
